package com.bolt.admin.codegenerator;


import com.bolt.common.utils.StrUtil;
import com.bolt.convention.data.code.CommonCode;
import com.bolt.convention.exception.ServiceException;
import com.bolt.support.codegenerator.db.AbstractTableFactory;
import com.bolt.support.codegenerator.db.TableFactory;
import com.bolt.support.codegenerator.model.Table;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Administrator on 2018/11/5.
 */
@Slf4j
public class CodeGeneratorService {


    private TableFactory tableFactory;


    public CodeGeneratorService(String url, String username, String password, String driverClass) {
        tableFactory = AbstractTableFactory.getInstance(url, username, password, driverClass);
    }


    private Configuration getConfiguration() throws TemplateException {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_25);
        // 设置FreeMarker的模版文件位置
        cfg.setClassForTemplateLoading(CodeGeneratorService.class, "/code_templates");
        cfg.setNumberFormat("###############");
        cfg.setBooleanFormat("true,false");
        cfg.setDefaultEncoding("UTF-8");
        return cfg;
    }


    /**
     * 取出数据所有表描述
     *
     * @return
     */
    public Map <String, String> getAllTables() {
        try {
            return tableFactory.getAllTables();
        } catch (Exception ex) {
            log.error("get all table list error", ex);
            throw new ServiceException();
        }
    }


    public Table getTable(String tableName) {
        try {
            return tableFactory.getTable(tableName);
        } catch (SQLException ex) {
            log.error("find table " + tableName + "no found", ex);
            throw new ServiceException();
        }
    }

    /**
     * 生成BO类
     *
     * @param table
     * @return
     */
    public void generatorDTOByTable(Table table, String rootDir, String basePackage, String fileName) {
        generatorJavaCode(table, "/Dto.ftl", rootDir, basePackage, "dto", fileName, true);
    }

    /**
     * 生成DO类
     *
     * @param table
     * @param rootDir
     * @param basePackage
     * @param fileName
     */
    public void generatorDOByTable(Table table, String rootDir, String basePackage, String fileName) {
        generatorJavaCode(table, "/EntityDO.ftl", rootDir, basePackage, "model", fileName, true);
    }

    /**
     * 生成Entity类
     *
     * @param table
     * @param rootDir
     * @param basePackage
     * @param fileName
     */
    public void generatorEntityByTable(Table table, String rootDir, String basePackage, String fileName) {
        generatorJavaCode(table, "/Entity.ftl", rootDir, basePackage, "entity", fileName, true);
    }

    /**
     * 生成Converter类
     *
     * @param table
     * @param rootDir
     * @param basePackage
     * @param fileName
     */
    public void generatorConverterByTable(Table table, String rootDir, String basePackage, String fileName) {
        generatorJavaCode(table, "/Converter.ftl", rootDir, basePackage, "dto.converter", fileName, true);
    }

    /**
     * 生成控制类
     *
     * @param table
     * @return
     */
    public void generatorController(Table table, String rootDir, String basePackage, String fileName) {
        generatorJavaCode(table, "/Controller.ftl", rootDir, basePackage, "controller", fileName, true);
    }

    /**
     * 生成控制类
     *
     * @param table
     * @return
     */
    public void generatorRestController(Table table, String rootDir, String basePackage, String fileName) {
        generatorJavaCode(table, "/RestController.ftl", rootDir, basePackage, "controller", fileName, true);
    }

    /**
     * 生成Repository
     *
     * @param table
     * @return
     */
    public void generatorRepository(Table table, String rootDir, String basePackage, String fileName) {
        generatorJavaCode(table, "/Repository.ftl", rootDir, basePackage, "repository", fileName, true);
    }

    /**
     * 生成mapper.xml
     *
     * @param table
     * @param rootDir
     * @param basePackage
     * @param fileName
     */
    public void generatorMapper(Table table, String rootDir, String basePackage, String fileName) {
        String module = StrUtil.subAfter(basePackage, ".", true);
        generatorJavaCode(table, "/mapper.xml.ftl", rootDir, basePackage, "mapper." + module, fileName, false);
    }


    /**
     * 生成业务类
     *
     * @param table
     * @return
     */
    public void generatorService(Table table, String rootDir, String basePackage, String fileName) {
        generatorJavaCode(table, "/Service.ftl", rootDir, basePackage, "service", fileName, true);
    }

    public void generatorServiceImpl(Table table, String rootDir, String basePackage, String fileName) {
        generatorJavaCode(table, "/ServiceImpl.ftl", rootDir, basePackage, "manager.impl", fileName, true);
    }

    private void generatorJavaCode(Table table, String templateName, String rootDir, String basePackage, String subDir, String fileName, boolean generatorPackagePath) {
        try {
            if (StrUtil.isBlank(basePackage)) {
                throw new ServiceException(CommonCode.INVALID_ARGS);
            }
            Path path;
            if (generatorPackagePath) {
                String packageDirName = basePackage.replace('.', '/');
                path = Paths.get(rootDir, packageDirName, subDir.replace('.', '/'));
            } else {
                String disPath = subDir.replace('.', '/');
                path = Paths.get(rootDir, disPath);
            }
            Configuration cfg = getConfiguration();
            Map <String, Object> root = new HashMap <>();
            root.put("package", basePackage);
            root.put("table", table);

            process(cfg.getTemplate(templateName), root, path.toString(), fileName);

        } catch (Exception ex) {
            log.error("generate error", ex);
            throw new ServiceException();
        }
    }


    /**
     * 根据模板文件生成代码文件
     *
     * @param template
     * @param root
     * @param rootDir
     * @param fileName
     * @throws IOException
     */
    public void process(Template template, Map <String, Object> root, String rootDir, String fileName) throws IOException {
        if ((rootDir + fileName).length() > 300) {
            throw new ServiceException();
        }
        File newsDir = new File(rootDir);
        if (!newsDir.exists()) {
            newsDir.mkdirs();
        }
        log.info("Write to file: " + rootDir + fileName);
        Writer out = new OutputStreamWriter(
                new FileOutputStream(rootDir + File.separator + fileName), "UTF-8");
        try {
            template.process(root, out);
        } catch (Exception ex) {
            throw new ServiceException();
        } finally {
            out.close();
        }
    }

    /**
     * 根据模板文件生成代码
     *
     * @param template
     * @param root
     * @return
     */
    private String process(Template template, Map <String, Object> root) {
        StringWriter output = new StringWriter();
        try {
            template.process(root, output);
            return output.toString();
        } catch (Exception ex) {
            throw new ServiceException();
        } finally {
            try {
                output.close();
            } catch (IOException ex) {
                throw new ServiceException();
            }
        }
    }


}
